package org.NooLab.glue.storage;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
 
import java.io.FileOutputStream;
 
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.SortedMap;
import java.util.TreeMap;

import org.NooLab.utilities.files.DFutils;
import org.NooLab.utilities.logging.PrintLog;

import org.NooLab.glue.MessageBoard;
import org.NooLab.glue.components.MessageBoardFactory;
import org.NooLab.glue.components.MessageBoardShareIntf;
import org.NooLab.glue.msgframe.Notifications;
import org.NooLab.glue.net.HostingData;
import org.NooLab.glue.storage.MsgBoardInternalData;
import org.NooLab.glue.transaction.Transaction;




public class MsgBoardDataStorage {

	public static final String BoardsTransferBufferfilesPrefix = "~rn-" ;
	
	MessageBoardShareIntf msgBoard;
	MsgBoardInternalData msgboardData ;
	MessageBoardFactory factory;
	
	int msgBoardPersistenceType = 0;
	String filename = "" ;
	
	private String homePath="" ;
	String cfgHomePath = "" ;
	String dataStoragePath = "" ;
	
	String receiptstorageFilename = "recv.dat"; 

	boolean keepAllBuffers = false;
	
	DFutils fileutil = new DFutils() ;
	PrintLog out ;
	
	public MsgBoardDataStorage( MessageBoardFactory factory, PrintLog outprn){
		
		int r ;
		long[] receiptValues;

		this.factory = factory;
		out = outprn;
		
		cfgHomePath = factory.getFactoryProperties().getConfigPath() ;
		homePath = factory.getFactoryProperties().getHomePath() ; //fileutil.getParentDir( cfgHomePath );
		
		
	}
	
	public MsgBoardDataStorage( MessageBoardShareIntf mbShare, String cfgHomepath, PrintLog outprn){
		int r ;
		long[] receiptValues;
		
		msgBoard = mbShare;
		
		out = outprn;
		
		cfgHomePath = cfgHomepath; // msgboard.getConfigHomePath() ;
		filename = fileutil.createPath(cfgHomePath, "msgboard.cfg") ;
		
		receiptstorageFilename = fileutil.createPath( cfgHomePath, receiptstorageFilename);
		
		
		homePath = fileutil.getParentDir( cfgHomePath );
		
		dataStoragePath = fileutil.createPath(homePath, "transactData/");
			
		/* MOVED REFERENCE !!
		// there is always an object around, filled with initial data from main object...
		msgboardData = new MsgBoardInternalData(  );
		msgboardData.setMsgBoardInternalData(msgBoard); // TODO OUTSOURCE
		*/
		
		mbShare.setMsgBoardStorage(this) ;
		
		msgboardData = new MsgBoardInternalData(  );
		msgboardData.setMsgBoardInternalData( mbShare );
		
		r = loadMsgBoardData();
		
		// accessing external persistence storage in config directory
		receiptValues = getLastGlobalReceiptValue();
		
		mbShare.getGlobalReceipts().setGlobalReceiptID( receiptValues[0] ) ;
		
		mbShare.getGlobalReceipts().setGlobalRegisteredID( receiptValues[1] ) ;
		
	}
	
	
	
	public void fixLastGlobalReceiptValue( long receiptvalue, long registeredIDvalue ){
		
		try{ 
			if (fileutil.fileexists(receiptstorageFilename)){
				if (fileutil.fileexists(receiptstorageFilename+"-bak")){
					fileutil.deleteFile(receiptstorageFilename+"-bak");
				}
				
				fileutil.renameFile(receiptstorageFilename, receiptstorageFilename+"-bak");
				if (fileutil.fileexists(receiptstorageFilename)){
					fileutil.deleteFile(receiptstorageFilename) ;
				}
			}
			fileutil.writeFileSimple( receiptstorageFilename, ""+receiptvalue +"\n"+registeredIDvalue) ;
			
		}catch(Exception e){
			
		}
		 
		
	}

	public long[] getLastGlobalReceiptValue( ){
		
		long receiptValues[] = new long[]{1,1} ;
		
		String str;
		String[] strs = new String[]{"1","1"};
		
		
		if (fileutil.fileexists(receiptstorageFilename)==false){
			return receiptValues;
		}
		try {
			str = fileutil.readFile2String(receiptstorageFilename).trim() ;
			
			if (str.contains("\n")){
				strs = str.split("\n") ;
			}
			
			receiptValues[0] = Long.parseLong(strs[0].trim()) ;
			receiptValues[1] = Long.parseLong(strs[1].trim()) ;
			
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		
		return receiptValues;
	}

	
	public int loadMsgBoardData(){
		int result = -1;
		
		result = loadMsgBoardDataObj();
		
		if (result==0){
			msgboardData.retrieveData();
		}
		
		return result ;
	}
	
	
	public int saveMsgBoardData(){
		int result=-1;
		
		fixLastGlobalReceiptValue( msgBoard.getGlobalReceipts().getGlobalReceiptID() , 
								   msgBoard.getGlobalReceipts().getGlobalRegisteredID() );
		 
		msgboardData.deliverData() ;
		result = saveMsgBoardDataObj() ;
		
		return result ;
	}



	private int loadMsgBoardDataObj(){  
		int result=-1;
		MsgBoardInternalData mbd;
		ObjectInputStream objistream = null ;
		FileInputStream fileIn = null ;
		BufferedInputStream bins = null ;

		try{
			

			if (fileutil.fileexists(filename)==false){
				return -1 ;
			}
			
			fileIn = new FileInputStream(filename);
			bins = new BufferedInputStream(fileIn);
			objistream = new ObjectInputStream(bins);

			mbd = (MsgBoardInternalData) objistream.readObject();
												out.print(3, "started: connection to <MsgBoardDataStorage>, file: "+filename+"");
												out.print(5, "Closing all input streams...");
			result = 0;
			
		}catch(Exception e){
			result = -3;
			e.printStackTrace();
		}finally{
			try{

				if (objistream!=null) objistream.close();
				if (bins!=null) bins.close();
				if (fileIn!=null) fileIn.close();
		
			}catch(Exception e){
			}
		}
				
		return result ;
	}


	public void storeHostingData( HostingData hdata, String identifier ){
	
		String filename ;
		
		
		filename = createRouteHostStorageFilename(identifier);
	
		storeTransactionDataObj( hdata, filename);
		
	}

	public HostingData readHostingData( String identifier ){
		
		String filename ;
		HostingData hdata ;
		
		filename = createRouteHostStorageFilename(identifier);
	
		if (fileutil.fileexists(filename)==false){
			hdata = new HostingData();
			storeHostingData(hdata,identifier) ;
		}else{
			hdata = (HostingData) readTransactionDataObj(filename) ;
		}
		
		return hdata;
	}

	
	public String createRouteHostStorageFilename( String identifier ){
		
		String filename="", path;
		
		path = fileutil.createPath( this.cfgHomePath,"routes/");
		
		filename = fileutil.createPath( path, "~regroutes-"+identifier+".dat");
		
		return filename;
	}
	
	
	
	public void saveNotificationsDictionary( Notifications notes ){
		
		String filename;
		String path, str="";
		int n;
		
		try{
			n = notes.size() ;
			path = fileutil.createPath( this.homePath,"transferBuffer/");
			
			filename = fileutil.createPath( path, "~resultsnnotes.lst") ;
			
			storeTransactionDataObj( notes, filename) ;
			
			
		}catch(Exception e){
			
		}
		
	}
	
	/**
	 * TODO: filelock, waiting mechanism for preventing collisions
	 * 
	 * @param role
	 * @param quality
	 * @param openTransactions
	 */
	synchronized public void saveParticipantstransactions( String name, String role, String quality, SortedMap<Long, String> openTransactions ){
								               //           "SOURCE",       "open",    mbf.openTransactions ){
		String filename;
		
		filename = createTransactionDictFileName(name,role,quality) ;
		

		storeTransactionDataObj( openTransactions, filename) ;
		
		
	}
	
	@SuppressWarnings("unchecked")
	public TreeMap<Long,String> getParticipantstransactions( String name,String role, String quality){ // "SOURCE", "open", mbf.openTransactions ){
		TreeMap<Long,String> tDict = null;
		String filename;
		
		filename = createTransactionDictFileName(name,role,quality) ;
		
		tDict = (TreeMap<Long, String>) readTransactionDataObj(filename);
		
		return tDict;
	}
	
	protected String createTransactionDictFileName( String name, String role, String quality){
		String path, str="", filename = "";


		name = name.toLowerCase().trim() ;
		path = fileutil.createPath( this.homePath,"transferBuffer/"+name+"/");
		
		// transactions could be interrupted, resulting in different instanceIDs...
		str = str + role.toLowerCase().substring(0,2) ;
		
		filename = "~tstor-" + quality +"_"+ str+".dict";
		filename = fileutil.createPath( path, filename);
			
		return filename;
	}
	
	/**
	 * 
	 * 
	 * @param transaction
	 * @param callersRole will cause the usage of a separate sub-directory 
	 * @return
	 */
	public String saveTransactionResultsObj(   Transaction transaction, String callersRole  ){
		
		boolean rB = false;
		String  filename="";
		
		try{
			// note that the transaction also contains the ID of the originating SOURCE instance !
			filename = createTransactionBufferfileName(transaction, callersRole) ;
			
			// 
		    if (filename.length()>0){
		    	
		    	storeTransactionDataObj( transaction, filename) ;
		    	
		    	if (fileutil.fileexists(filename)==false){
		    		out.printErr(2, "Problem saving results buffer file: storing failed, intended filename = "+filename);
		    		filename = "";	
		    	}
		    } else{
		    	out.printErr(2, "Problem saving results buffer file: no filename available");
		    }
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return filename;
	}
	
	
	public String createTransactionBufferfileName( Transaction transaction, String callersRole ){
		String str,path,filename="";
		
		if (transaction==null){
			return filename;
		}
		callersRole = callersRole.toLowerCase().trim() ;
		if (callersRole.length()>0){
			callersRole = callersRole+"/" ;
		}
		path = fileutil.createPath( this.homePath,"transferBuffer/"+callersRole);
		
		str = ""+transaction.getTransactionID()+"-" ;
		// str = str + ""+transaction.getInstanceID() +"-";
		// transactions could be interrupted, resulting in different instanceIDs...
		// str = str + transaction.getRoleOfInitializer().toLowerCase().substring(0,2) ;
		   str = str + callersRole.substring(0,2) ;  // str = ""+transaction.getTransactState() ;
		
		filename = BoardsTransferBufferfilesPrefix + str + ".btmp"; // ~rn_[123]_so.btmp
		filename = fileutil.createPath( path, filename);
		
		return filename;
	}
	
	public String createTransactionBufferfileName( Long iid, long tid, String role, String callersRole){
	
		return createTransactionBufferfileName( iid, tid, role, callersRole, 1) ;
	}
	
	public String createTransactionBufferfileName( Long iid, long tid, String role, String callersRole, int stage ){
		String filename="",str,path , stageDir;
		
		callersRole = callersRole.toLowerCase();
		if (callersRole.length()>0){
			callersRole = callersRole+"/" ;
		}
		role = role.toLowerCase();
		
		if (stage<=1){
			stageDir = "transferBuffer/" ;
		}else{
			stageDir = "transactData/" ;
		}
		
		path = fileutil.createPath( this.homePath,stageDir+callersRole);
		
		str = ""+tid+"-" ;
		// str = str + ""+ iid +"-";
		if (role.length()>0){
			str = str + role.toLowerCase().substring(0,2) ;
		}
		// str = ""+transaction.getTransactState() ;
		
		
		filename = BoardsTransferBufferfilesPrefix + str + ".btmp";
		filename = fileutil.createPath( path, filename);
		
		return filename;
	}
	
	
	public boolean removeBufferFile( String bufferfilename){
		boolean rB=false;
		// dependent on settings, if archiving then just moving to another dir
		
		if (fileutil.fileexists(bufferfilename)){
			fileutil.deleteFile(bufferfilename) ;
			out.delay(5);
		}
		if (fileutil.fileexists(bufferfilename)==false){
			rB=true;
		}
		return rB;
	}
	
	
	private int saveMsgBoardDataObj(){  
		int result=-1;
		
		FileOutputStream fileOut;
		BufferedOutputStream bout;
		ObjectOutputStream objout ;

		
		if (fileutil.fileexists(filename)==false){
			
		}
	
		
		try{
			
			 										out.print(5,"Writing msgboard data-object physically (1)... ");
			    fileOut = new FileOutputStream( filename );
			    bout = new BufferedOutputStream(fileOut);
	            objout = new ObjectOutputStream(bout);

	            									 out.print(5,"Writing msgboard data-object physically (2)... ");
	            objout.writeObject( msgboardData );

	            									 out.print(5,"closing all output streams... ");
	            objout.close();
	            bout.close();
	            fileOut.close();
	            result=0 ;
	            									 out.print(5,"output streams closed. ");

			
		}catch(Exception e){
			result = -3;
			e.printStackTrace() ;
		}
		
		
		return result ;
	}

	
	
	public Object readTransactionDataObj( String filename) {
		Object dataObj=null;
		
		int result=-1;
		 
		ObjectInputStream objistream = null ;
		FileInputStream fileIn = null ;
		BufferedInputStream bins = null ;

		try{
			

			if (fileutil.fileexists(filename)==false){
				return -1 ;
			}
			
			fileIn = new FileInputStream(filename);
			bins = new BufferedInputStream(fileIn);
			objistream = new ObjectInputStream(bins);

			dataObj =   objistream.readObject();
		 
												out.print(5, " Closing all input streams...");
			result = 0;
			
		}catch(Exception e){
			result = -3;
			dataObj=null;
			// e.printStackTrace(); 
			out.printErr(1, "Crashing File Read Error in MsgBoardDataStorage.readTransactionDataObj(), \n"+
							"file "+filename+"\n"+
							"message = "+e.getMessage()) ;
		}finally{
			try{

				if (objistream!=null) objistream.close();
				if (bins!=null) bins.close();
				if (fileIn!=null) fileIn.close();
		
			}catch(Exception e){
				dataObj=null;
			}
		}
		return dataObj;
	}
	
	synchronized public int  storeTransactionDataObj( Object dataObj, String filename) {
		int result=-1;
		
		FileOutputStream fileOut;
		BufferedOutputStream bout;
		ObjectOutputStream objout ;

 
		try{
			
			 										out.print(5,"Writing msgboard transaction data ... "+filename);
			    fileOut = new FileOutputStream( filename );
			    bout = new BufferedOutputStream(fileOut);
	            objout = new ObjectOutputStream(bout);

	            									 
	            objout.writeObject( dataObj );

	            									 
	            objout.close();
	            bout.close();
	            fileOut.close();
	            result=0 ;
	            									 out.print(5,"output streams closed. ");
			    if (fileutil.fileexists(filename)==false){
			    	result = -3;
			    }
		}catch(Exception e){
			result = -7;
			e.printStackTrace();
		}
		return result ;
	}
	
	public String createParticipantsResultBufferfileName( long instanceID, long transactionID ){
		String str,filename="" ;
		
		str = dataStoragePath;
		
		str = fileutil.createPath( str, "source/results/"+instanceID+"/");
		
		filename = "~rct-"+instanceID+"_"+transactionID+".tmp";	
		
		filename = fileutil.createPath( str, filename);
		
		return filename;
	}
	
	public String createTransactionDataObjTmpFilename( long transactID, long instanceID ){
		
		String path, str, filename="";
		
		if (transactID<=0){
			return "null" ;
		}
		
		if (dataStoragePath.length()==0){
			 
			dataStoragePath = fileutil.getTempDir("~noo.mb/data/") ;
		}
		
		if (fileutil.fileexists(dataStoragePath)==false){
			fileutil.createDir(dataStoragePath) ;
		}
		
		str = "~tdata_tid-"+transactID;
		
		if (instanceID>0){
			str = str +"_inid-"+instanceID ;
		}
		
		str = str +".obj" ;
		
		filename = fileutil.createPath( dataStoragePath, str) ;
		
		return filename ;
	}
	
	
	// ------------------------------------------------------------------------
	
	public int getMsgBoardPersistenceType() {
		return msgBoardPersistenceType;
	}

	public void setMsgBoardPersistenceType(int msgBoardPersistenceType) {
		this.msgBoardPersistenceType = msgBoardPersistenceType;
	}

	public boolean getKeepAllBuffers() {
		return keepAllBuffers;
	}

	public void setKeepAllBuffers(boolean keepAllBuffers) {
		this.keepAllBuffers = keepAllBuffers;
	}

	public String getHomePath() {
		return homePath;
	}

	
	public void setHomePath(String homePath) {
		this.homePath = homePath;
	}

	public String getHibernationPath( String divertingString ){
		String path="";
		
		if ((divertingString==null)){
			divertingString = "" ;
		}
		if ((divertingString!=null) && (divertingString.length()>0)){
			divertingString = divertingString.toLowerCase()+"/";
		}
		path = getHomePath();
		path = fileutil.createPath( path, "hibernation/"+divertingString) ;
		
		return path ;
	}

	public String getCfgHomePath() {
		return cfgHomePath;
	}



	public void setCfgHomePath(String cfgHomePath) {
		this.cfgHomePath = cfgHomePath;
	}



	public String getDataStoragePath() {
		return dataStoragePath;
	}



	public void setDataStoragePath(String dataStoragePath) {
		this.dataStoragePath = dataStoragePath;
	}

	
}


